package service

import (
	"fmt"
	"multimedia/utils"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"syscall"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
)

type WhiteBoardInfo struct {
	RecordName string
	StartTime  int64
	StopTime   int64
	UserId     string
}
type Record struct {
	WhiteboardList []*WhiteBoardInfo
	StartTime      int64
	Meetingpath    string
	sync.RWMutex
}
type RecordMgr struct {
	RecordList map[string]*Record
	sync.RWMutex
}

func NewRecord() *Record {
	return &Record{
		WhiteboardList: make([]*WhiteBoardInfo, 0, 3),
	}
}
func (r *Record) UpdateWhiteboardRecord(recordfile string, starttime int64, userid string) int {
	r.Lock()
	defer r.Unlock()
	w := &WhiteBoardInfo{
		RecordName: recordfile,
		StartTime:  starttime,
		UserId:     userid,
	}
	r.WhiteboardList = append(r.WhiteboardList, w)
	return len(r.WhiteboardList)
}
func (r *Record) GetRecordInfo() ([]*WhiteBoardInfo, int64, string) {
	r.RLock()
	defer r.RUnlock()
	return r.WhiteboardList, r.StartTime, r.Meetingpath
}
func (r *Record) GetUploadPath() string {
	r.RLock()
	defer r.RUnlock()
	size := len(r.WhiteboardList)
	if size == 0 {
		return ""
	}
	return r.WhiteboardList[size-1].RecordName
}
func (r *Record) StopRecord(stoptime int64) int64 {
	r.Lock()
	defer r.Unlock()
	whiteboard := r.WhiteboardList[len(r.WhiteboardList)-1]
	whiteboard.StopTime = stoptime
	return whiteboard.StartTime
}
func NewRecordMgr() *RecordMgr {
	return &RecordMgr{
		RecordList: make(map[string]*Record),
	}
}

func (r *RecordMgr) CreateRecord(roomid, meetingfile string, currenttime int64) (*Record, bool) {
	r.Lock()
	defer r.Unlock()
	record, ok := r.RecordList[roomid]
	if !ok {
		record = NewRecord()
		record.StartTime = currenttime
		record.Meetingpath = meetingfile
		r.RecordList[roomid] = record
		return nil, false
	}
	record.Meetingpath = meetingfile
	return record, true
}
func (r *RecordMgr) GetOrCreateRecord(roomid string, currenttime int64) (*Record, int64, string) {
	r.Lock()
	defer r.Unlock()
	record, ok := r.RecordList[roomid]
	if !ok {
		record = NewRecord()
		record.StartTime = currenttime
		r.RecordList[roomid] = record
	}
	return record, record.StartTime, record.Meetingpath
}

func (r *RecordMgr) GetRecord(roomid string) *Record {
	r.RLock()
	defer r.RUnlock()
	record, ok := r.RecordList[roomid]
	if !ok {
		return nil
	}
	return record
}
func (r *RecordMgr) StopRecord(roomid string) *Record {
	r.RLock()
	defer r.RUnlock()
	record, ok := r.RecordList[roomid]
	if !ok {
		return nil
	}
	delete(r.RecordList, roomid)
	return record
}
func (s *Srv) WhiteBoardRecordStart(c *gin.Context) {
	var req utils.WhiteBoardRecordReq
	if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil {
		c.JSON(http.StatusOK, gin.H{"code": utils.ReqParamErr})
		return
	}
	startTime := time.Now().UnixMicro()
	record, RecordTime, meetingpath := s.Recordlist.GetOrCreateRecord(req.RoomId, startTime)
	num := record.UpdateWhiteboardRecord(req.UploadPath, startTime, req.UserId)
	meeting_duration := int(startTime-RecordTime) / 1000
	s.mysql.AddWhiteboardRecord(num, meeting_duration, startTime, meetingpath, req.RoomId, req.UserId, req.UploadPath)
}
func (s *Srv) WhiteBoardRecordstop(c *gin.Context) {
	var req utils.WhiteBoardRecordReq
	if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil {
		c.JSON(http.StatusOK, gin.H{"code": utils.ReqParamErr})
		return
	}
	record := s.Recordlist.GetRecord(req.RoomId)
	if record == nil {
		c.JSON(http.StatusOK, gin.H{"code": utils.ConferNoExist})
		return
	}
	stopTime := time.Now().UnixMicro()
	startTime := record.StopRecord(stopTime)
	duration := int(stopTime-startTime) / 1000
	s.mysql.UpdateWhiteboardDuration(duration, req.UploadPath)
}

func (s *Srv) RecordStart(roomid, meetingfile string) {
	startTime := time.Now().UnixMicro()
	record, exist := s.Recordlist.CreateRecord(roomid, meetingfile, startTime)
	if !exist {
		return
	}
	recordname := record.GetUploadPath()
	if recordname != "" {
		s.mysql.UpdateWhiteboardRecord(meetingfile, recordname)
	}
}
func (s *Srv) RecordStop(conferName string) {
	record := s.Recordlist.StopRecord(conferName)
	if record == nil {
		return
	}
	whiteboardrecord, starttime, meetingpath := record.GetRecordInfo()
	if len(whiteboardrecord) <= 0 {
		return
	}
	if whiteboardrecord[len(whiteboardrecord)-1].StopTime == 0 {
		stopTime := time.Now().UnixMicro()
		whiteboardrecord[len(whiteboardrecord)-1].StopTime = stopTime
		whiteboardstarttime := whiteboardrecord[len(whiteboardrecord)-1].StartTime
		recordpath := whiteboardrecord[len(whiteboardrecord)-1].RecordName
		duration := int(stopTime-whiteboardstarttime) / 1000
		s.mysql.UpdateWhiteboardDuration(duration, recordpath)
	}
	args := make([]string, 0)
	args = append(args, "-i", meetingpath)
	filterstr := ""
	out_pathname := meetingpath[0 : len(meetingpath)-len(filepath.Ext(meetingpath))]
	outfile := fmt.Sprintf("/data/apps/data/record_files/%s_bak.mp4", out_pathname)
	merge_out := fmt.Sprintf("/data/apps/data/record_files/%s", meetingpath)
	for k, v := range whiteboardrecord {
		recordname := fmt.Sprintf("%s.webm", v.RecordName)
		args = append(args, "-i", recordname)
		meeting_duration := int(v.StartTime-starttime) / 1000
		duration := int(v.StopTime-v.StartTime) / 1000
		if k == 0 {
			filterstr = fmt.Sprintf("[0][1]overlay=0:0:enable='between(t,%d,%d)'[out]", meeting_duration, duration)
		} else {
			filterstr = fmt.Sprintf("%s;[out][%d]overlay=0:0:enable='between(t,%d,%d)'[out]", filterstr, k+1, meeting_duration, duration)
		}
	}
	args = append(args, "-map", "0:0", "-filter_complex", filterstr, outfile)

	cmd := exec.Command("ffmpeg", args...)
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
	}

	err := cmd.Start()

	if err != nil {
		return
	}

	mergeDone := make(chan error)
	go func() {

		mergeDone <- cmd.Wait()
	}()

	select {
	case err = <-mergeDone:
	}
	if err != nil {
		return
	}
	err = os.Rename(outfile, merge_out)
}
